
/***************************************************************************
 * \file mysql.h
 * \date 08/07/2008
 * \author Chris Tonkinson
 * \note All set and get methods are defined inline by default
 *       for execution efficiency and has the side effect of also
 *       keeping the .cpp file clean and clutter free.
 ***************************************************************************/

// Inclusion guards...
#ifndef _H__MYSQL
#define _H__MYSQL

// Includes...
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <list>
#include <map>
#include <mysql/mysql.h>
#include <string>

// Exception codes...
#define DB_EXCEPTION_CANT_OPEN_CONFIG   1
#define DB_EXCEPTION_CANT_CONNECT       2
#define DB_EXCEPTION_UNKNOWN_CONFIG     3
#define DB_EXCEPTION_NOT_CONNECTED      4
#define DB_EXCEPTION_EMPTY_CONFIG_VALUE 5
#define DB_EXCEPTION_FAILED_ASSERTION   6

// Other options...
#define	CONNECT_TIMEOUT	5			      	// MySQL timeout (in seconds)
#define CONFIG_FILE		  "mysql.conf"	// File which holds configuration options

// Credential classes...
#define MYSQL_CREDENTIAL_CONFIG_FILE  0
#define MYSQL_CREDENTIAL_CONSTRUCTOR  1



//////////////////////////////////////////////////////////////////////////  MYSQL STRING  //////////////////////////////////////////////////////////////////////////
class mysql_string: public std::string {
  private:

  public:
    mysql_string( void ) { return; }
    mysql_string( const mysql_string& ref ) { this->assign( ref.data() ); }
    mysql_string( const char* cs ) { this->assign( cs ); return; }
    ~mysql_string( void ) { return; }

    operator bool               ( void ) const { return ( atof( this->c_str() ) != 0 || atoi( this->c_str() ) != 0 ); }
    operator signed short int   ( void ) const { return atol( this->c_str() ); }
    operator unsigned short int ( void ) const { return atol( this->c_str() ); }
    operator signed int         ( void ) const { return atoi( this->c_str() ); }
    operator unsigned int       ( void ) const { return atoi( this->c_str() ); }
    operator signed long int    ( void ) const { return atol( this->c_str() ); }
    operator unsigned long int  ( void ) const { return atol( this->c_str() ); }
    operator float              ( void ) const { return atof( this->c_str() ); }
    operator double             ( void ) const { return atof( this->c_str() ); }
    operator char               ( void ) const { return this->c_str()[0]; }
    operator const char*        ( void ) const { return this->c_str(); }
};

//////////////////////////////////////////////////////////////////////////  MYSQL ROW  //////////////////////////////////////////////////////////////////////////
template <class key_type, class val_type> class mysql_map: public std::map<key_type,val_type> {
  public:
    bool operator = ( const mysql_map<key_type,val_type>& src ) {
      this->std::map<key_type,val_type>::operator = ( src );
      return !this->empty();
    }
};

typedef mysql_map<std::string,mysql_string> ROW;

//////////////////////////////////////////////////////////////////////////  MYSQL EXCEPTION  //////////////////////////////////////////////////////////////////////////
class MysqlException {
  private:
    std::string     _message;

    void  setMessage( const std::string& message ) { _message = message; return; }

  public:
    MysqlException( void );
    MysqlException( const std::string& file, const unsigned long& line, const std::string& extra = "" );
    MysqlException( const MysqlException& ref );
    ~MysqlException( void );

    std::string    getMessage( void ) const { return _message; }
};



//////////////////////////////////////////////////////////////////////////  CACHE PIECE  //////////////////////////////////////////////////////////////////////////
class CachePiece {
  private:
    std::string _hostname;
    std::string _username;
    std::string _password;
    std::string _database;

    void  setHostname( const std::string& hostname ) { _hostname = hostname; return; }
    void  setUsername( const std::string& username ) { _username = username; return; }
    void  setPassword( const std::string& password ) { _password = password; return; }
    void  setDatabase( const std::string& database ) { _database = database; return; }

  public:
    CachePiece( void );
    CachePiece( const std::string& host, const std::string& name, const std::string& password, const std::string& database );
    CachePiece( const CachePiece& ref );
    ~CachePiece( void );

    std::string getHostname( void ) const { return _hostname; }
    std::string getUsername( void ) const { return _username; }
    std::string getPassword( void ) const { return _password; }
    std::string getDatabase( void ) const { return _database; }
};



//////////////////////////////////////////////////////////////////////////  MYSQL CACHE  //////////////////////////////////////////////////////////////////////////
class MysqlCache {
  private:
    std::map<std::string,CachePiece*>   _cache;

    std::map<std::string,CachePiece*>&  getCache( void ) { return _cache; }

  public:
    MysqlCache( void );
    MysqlCache( const MysqlCache& ref );
    ~MysqlCache( void );

    CachePiece* get( const std::string& config_file );
};



//////////////////////////////////////////////////////////////////////////  MYSQL CLASS  //////////////////////////////////////////////////////////////////////////
/**
 * \class Mysql
 * \brief 
 */
class Mysql {

  private:
    MYSQL*         _connection;    ///< A pointer to a MySQL C API connection object.
    MYSQL_RES*     _result;        ///< A pointer to a MySQL C API result object.
    MYSQL_ROW      _row;           ///< A MySQL C API row object.
    std::list<ROW> _data;          ///< Local STL storage for returned result sets.
    unsigned long  _numRows;       ///< The number of rows returned by the most recent select query.
    unsigned long  _affectedRows;  ///< The number of rows affected by the most recent insert, update, or delete query.
    unsigned long  _insertID;      ///< The primary key of the most recently inserted record.
    std::string    _hostname;      ///< The database host for the connection.
    std::string    _username;      ///< The username for the database connection.
    std::string    _password;      ///< The password for the database connection.
    std::string    _database;      ///< The default MySQL database to use.
    unsigned short _credentials;   ///< Where to get database credentials from.
    std::string    _filename;      ///< Configuration file to use, if any.

    static MysqlCache   _cache;

    /**
     * Returns a pointer to the static MysqlCache object.
     */
    MysqlCache* getCache( void ) { return &_cache; }

    /**
     * Changes the value of _connection.
     * \param connection A pointer to a MYSQL object.
     */
    void setConnection( MYSQL* connection ) { _connection = connection; return; }

    /**
     * Returns the value of _connection.
     */
    MYSQL* getConnection( void ) { return _connection; }

    /**
     * Changes the value of _result.
     * \param result A pointer to a MYSQL_RES object.
     */
    void setResult( MYSQL_RES* result ) { _result = result; return; }

    /**
     * Returns the value of _result.
     */
    MYSQL_RES* getResult( void ) { return _result; }

    /**
     * Changes the value of _row.
     * \param row A pointer to a MYSQL_ROW object.
     * \return The new value of _row.
     */
    MYSQL_ROW setRow( MYSQL_ROW row ) { return (_row = row); }

    /**
     * Returns the value of _row.
     */
    MYSQL_ROW getRow( void ) { return _row; }

    /**
     * Returns the given offset of _row.
     */
    char* getRow( int i ) { return _row[i]; }

    /**
     * Returns the internal storage list.
     */
    std::list<ROW>& getData( void ) { return _data; }

    /**
     * Changes the value of _numRows.
     * \param numRows A constant reference to an unsigned long.
     */
    void setNumRows( const unsigned long& numRows ) { _numRows = numRows; return; }

    /**
     * Changes the value of _affectedRows.
     * \param affectedRows A constant reference to an unsigned long.
     */
    void setAffectedRows( const unsigned long& affectedRows ) { _affectedRows = affectedRows; return; }

    /**
     * Changes the value of _insertID.
     * \param insertID A constant reference to an unsigned long.
     */
    void setInsertID( const unsigned long& insertID ) { _insertID = insertID; return; }

    /**
     * Changes the value of _credentials.
     * \param credentials A constant reference to an unsigned short.
     */
    void setCredentials( const unsigned short& credentials ) { _credentials = credentials; return; }

    /**
     * Returns the value of _credentials.
     */
    unsigned short getCredentials( void ) const { return _credentials; }

    /**
     * Changes the value of _filename.
     * \param filename A constant reference to an unsigned string.
     */
    void setFilename( const std::string& filename ) { _filename = filename; return; }

    /**
     * Reads connection information from the configuration file given.
     */
    void readConfiguration( void );

    /**
     * Initializes the MYSQL pointer.
     */
    void init( void );

    /**
     * Resets row counts and insert id's, and does other pre-query setup work.
     */
    void queryPrep( void ) { setNumRows( 0 ); setAffectedRows( 0 ); setInsertID( 0 ); return; }

    /**
     * Manages transfer of result sets to local memeory.
     */
    unsigned long handleResults( void );

    /**
     * Stores data from previous query into local memory.
     */
    void store( void );

  public:

    /**
     * Default constructor.
     */
    Mysql( std::string config_file = "" );

    /**
     * Initializing constructor.
     * \param hostname A constant reference to a std::string object.
     * \param username A constant reference to a std::string object.
     * \param password A constant reference to a std::string object.
     * \param database A constant reference to a std::string object.
     */
    Mysql( const std::string& hostname, const std::string& username, const std::string& password, const std::string& database, bool autoconnect = true );

    /**
     * Destructor.
     */
    ~Mysql( void );

    /**
     * Changes the value of _hostname.
     * \param hostname A constant reference to a std::string.
     */
    void setHostname( const std::string& hostname ) { _hostname = hostname; return; }

    /**
     * Returns the value of _hostname.
     */
    std::string getHostname( void ) const { return _hostname; }

    /**
     * Changes the value of _username.
     * \param username A constant reference to a std::string.
     */
    void setUsername( const std::string& username ) { _username = username; return; }

    /**
     * Returns the value of _username.
     */
    std::string getUsername( void ) const { return _username; }

    /**
     * Changes the value of _password.
     * \param password A constant reference to a std::string.
     */
    void setPassword( const std::string& password ) { _password = password; return; }

    /**
     * Returns the value of _password.
     */
    std::string getPassword( void ) const { return _password; }

    /**
     * Changes the value of _database.
     * \param database A constant reference to a std::string.
     */
    void setDatabase( const std::string& database ) { _database = database; return; }

    /**
     * Returns the value of _database.
     */
    std::string getDatabase( void ) const { return _database; }

    /**
     * Returns the value of _filename.
     */
    std::string getFilename( void ) const { return _filename; }

    /**
     * Connects to the database with pre-stored credentials...
     */
    bool connect( void );

    /**
     * Performs a generic query on the database...
     */
    unsigned long query( const std::string& query );

    /**
     * Performs a select query on the database...
     */
    unsigned long select( const std::string& query );

    /**
     * Performas an insert query on the database...
     */
    unsigned long insert( const std::string& query );

    /**
     * Performas an update query on the database...
     */
    unsigned long update( const std::string& query );

    /**
     * Performas a delete query on the database...
     */
    unsigned long remove( const std::string& query );

    /**
     * Returns the value of _numRows.
     */
    unsigned long getNumRows( void ) const { return _numRows; }

    /**
     * Returns the value of _affectedRows.
     */
    unsigned long getAffectedRows( void ) const { return _affectedRows; }

    /**
     * Returns the value of _insertID.
     */
    unsigned long getInsertID( void ) const { return _insertID; }

    /**
     * Returns whether or not there is locally stored data left to fetch.
     */
    bool moreRows( void );

    /**
     * Returns the next row from locally stored data, or NULL
     * if there is nothing left to return.
     */
    ROW fetch( void );

    /***********************************************************
     *                     Static Methods                      *
     **********************************************************/
    /**
     * Escapes the target string to be legal for MySQL
     */
    static std::string addslashes( const std::string& str );

    /**
     * Works just like PHP's trim()
     */
    static std::string trim( const std::string& str );

};



#endif // #ifndef _H__MYSQL

